#include "RHIFence.h"
#include "../../Game/Process/RenderProcess.h"

RHIFence::BuilderDetails::BuilderDetails()
{
	isSignaled = true;
	count = GRenderState->GetFrameCount();
}

RHIFence::Builder::Builder() noexcept = default;
RHIFence::Builder::~Builder() noexcept = default;
RHIFence::Builder::Builder(RHIFence::Builder const& rhs) noexcept = default;
RHIFence::Builder::Builder(RHIFence::Builder && rhs) noexcept = default;
RHIFence::Builder& RHIFence::Builder::operator=(RHIFence::Builder const& rhs) noexcept = default;
RHIFence::Builder& RHIFence::Builder::operator=(RHIFence::Builder && rhs) noexcept = default;


RHIFence::Builder& RHIFence::Builder::SetSignaled(bool isSignaled) noexcept
{
	mImpl->isSignaled = isSignaled;
	return *this;
}

RHIFence::Builder& RHIFence::Builder::SetCount(uint32 count) noexcept
{
	mImpl->count = count;
	return *this;
}

RefCountPtr<RHIFence> RHIFence::Builder::Build()
{
	assert(IsInRenderThread());
	return RefCountPtr<RHIFence>(NewObject<RHIFence>(*mImpl));
}

RefCountPtr<RHIFence> RHIFence::Builder::Build(RenderProcess * process, const std::string & name)
{
	assert(IsInRenderThread());
	RHIFence* fence = NewObject<RHIFence>(*mImpl);
	process->AddFence(name, fence);
	return RefCountPtr<RHIFence>(fence);
}

void RHIFence::ReleaseResource()
{
	if (IsInRenderThread())
	{
		if (mProcess != nullptr)
		{
			mProcess->RemoveFence(mName, this);
		}
		DeleteObject<RHIFence>(this);
	}
	else
	{
		AsyncTask(GRenderThreadId, [this]() {
			if (mProcess != nullptr)
			{
				mProcess->RemoveFence(mName, this);
			}
			DeleteObject<RHIFence>(this);
			});
	}
}

RHIFence::~RHIFence()
{
	for (uint32 i = 0; i < mFences.size(); ++i)
	{
		GRenderState->DelayGCFence(mFences[i]);
	}
}

VkFence RHIFence::GetFence(uint32 index) const
{
	if (index == UINT32_MAX)
	{
		assert(mFences.size() == GRenderState->GetFrameCount() || mFences.size() == 1);
		return mFences.size() == 1 ? mFences[0] : mFences[GRenderState->GetFrameIndex()];
	}
	assert(index < mFences.size());
	return mFences[index];
}

bool RHIFence::IsSignaled(uint32 index /*= UINT32_MAX*/) const
{
	VkFence fence = GetFence(index);

	VkResult result = vkGetFenceStatus(GVulkanRHI->GetLogicalDevice(), fence);
	if (result == VK_SUCCESS)
		return true;
	if (result == VK_NOT_READY)
		return false;

	// VK_ERROR_DEVICE_LOST
	assert(0);
	return false;
}

void RHIFence::Wait(uint32 index)
{
	VkFence fence = GetFence(index);

	vkWaitForFences(GVulkanRHI->GetLogicalDevice(), 1, &fence, VK_TRUE, (std::numeric_limits<uint64>::max)());
}

void RHIFence::Reset(uint32 index)
{
	VkFence fence = GetFence(index);

	vkResetFences(GVulkanRHI->GetLogicalDevice(), 1, &fence);
}

RHIFence::RHIFence(BuilderDetails & builderDetails)
{
	VkFenceCreateInfo fenceCreateInfo{};
	fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
	fenceCreateInfo.flags = builderDetails.isSignaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0;

	mFences.resize(builderDetails.count, VK_NULL_HANDLE);
	for (uint32 i = 0; i < builderDetails.count; ++i)
	{
		VK_CHECK_RESULT(vkCreateFence(GVulkanRHI->GetLogicalDevice(), &fenceCreateInfo, nullptr, &mFences[i]));
	}
}


